﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Defilade
{
    public abstract class Coordinate
    {
        /// <summary>
        /// Creates a new Coordinate with the location [0.0.0]
        /// </summary>
        protected Coordinate()
        {
            Metres = 0;
        }

        /// <summary>
        /// Creates a new Coordinate from an encoded string in the format 'DD.MM.SS' (Degrees.Metres.Seconds)
        /// </summary>
        /// <param name="coordinateString"></param>
        protected Coordinate(String coordinateString)
        {
            string[] tmp;
            tmp = coordinateString.Split('.');
            Degrees = double.Parse(tmp[0]);
            Minutes = double.Parse(tmp[1]);
            Seconds = double.Parse(tmp[2]);
        }

        /// <summary>
        /// Creates a new Coordinate at the specified location
        /// </summary>
        /// <param name="degrees"></param>
        /// <param name="minutes"></param>
        /// <param name="seconds"></param>
        protected Coordinate(double degrees, double minutes, double seconds)
        {
            Degrees = degrees;
            Minutes = minutes;
            Seconds = seconds;
        }

        /// <summary>
        /// The magnitude of this coordinate (in metres)
        /// </summary>
        public double Metres
        {
            get
            {
                return ((Degrees * 3600) + (Minutes * 60) + Seconds) * MetresPerSecond();
            }
            set
            {
                double coordInSeconds = value / MetresPerSecond();
                Degrees = Math.Floor(coordInSeconds / 3600);
                coordInSeconds -= Degrees * 3600;
                Minutes = Math.Floor(coordInSeconds / 60);
                coordInSeconds -= Minutes * 60;
                Seconds = Math.Round(coordInSeconds, 0);
            }
        }

        /// <summary>
        /// The number of degrees in this coordinate
        /// </summary>
        public double Degrees
        {
            get;
            set;
        }

        /// <summary>
        /// The number of minutes in this coordinate
        /// </summary>
        public double Minutes
        {
            get;
            set;
        }

        /// <summary>
        /// The number of seconds in this coordinate
        /// </summary>
        public double Seconds
        {
            get;
            set;
        }

        /// <summary>
        /// Determines if a given string represents a valid coordinate or not (ie. if it is in the form 'DD.MM.SS')
        /// </summary>
        /// <param name="candidate">the string to check for validity</param>
        /// <returns>true if a valid coodinate can be built from the string, false otherwise</returns>
        public static bool IsValid(String candidate)
        {
            Regex coordRegex = new Regex(@"^[0-9][0-9]?\.[0-5]?[0-9]\.[0-5]?[0-9]$");
            return coordRegex.IsMatch(candidate);
        }

        /// <summary>
        /// Generates a string representing this location's Latitude in the format "DD.MM.SS"
        /// </summary>
        public override String ToString()
        {
            return String.Format("{0}.{1}.{2}", Degrees, Minutes, Seconds);
        }

        /// <summary>
        /// Function that defines how many metres are in a second of this coordinate type
        /// </summary>
        /// <returns></returns>
        protected abstract double MetresPerSecond();
    }
}
